package net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import message.Login;
import message.Logout;
import message.Message;
import message.Packet;
import message.PacketType;
import message.Registration;
import message.StatusNotification;
import message.handler.StatusHandler;

import org.w3c.dom.Document;

import comunity.Community;
import comunity.Status;

public class UserReceiver implements Runnable {
	Socket socket;
	String username;
	boolean running;

	public UserReceiver(Socket socket) {
		this.socket = socket;
		this.running = true;
	}

	public void run() {
		System.out.println("Started");
		Community community = Community.obtainCommunity();
		while (running) {
			try {
				if (username == null)
					this.firstConnection();
				else
					this.otherConnections();
			} catch (IOException e) {
				e.printStackTrace();
				running = false;
			} catch (Throwable t) {
				t.printStackTrace();
				running = false;
			}
		}
		System.out.println("[Err] Exiting form userReceveir");
		if (username != null && community.contains(username)) {
			community.setUserStatus(username, Status.OFFLINE);
			new Thread(new StatusHandler(username, Status.OFFLINE)).start();		
			
			if (community.existUserSocket(username)
					&& !community.isUserSocketClosed(username))
				try {
					community.closeUserSocket(username);
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	private void firstConnection() throws IOException {
		System.out.println("FirstConnection");
		PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
		BufferedReader in = new BufferedReader(new InputStreamReader(
				socket.getInputStream()));

		String senderAddress = socket.getInetAddress().getHostAddress();

		System.out.println("[in] waiting for data...");
		String packetRappresentation = in.readLine();
		if (packetRappresentation == null) {
			System.out.println("Null packet");
			this.running = false;
			return;
		}
		System.out.println("[in] " + packetRappresentation);
		Packet packet = this.createPacket(packetRappresentation);

		int result = packet.handlePacket(senderAddress);
		out.write(result);
		out.flush();

		Community community = Community.obtainCommunity();
		if (result == Packet.OK && community.contains(username)) {
			community.setUserSocket(username, socket);
		}/*
		 * else if (result != Packet.OK && result != Message.OFFLINE_RECEIVER) {
		 * running = false; }
		 */
	}

	private void otherConnections() throws IOException {
		Community community = Community.obtainCommunity();
		PrintWriter out = community.getUserPrintWriter(username);
		BufferedReader in = community.getUserBufferedReader(username);

		String senderAddress = socket.getInetAddress().getHostAddress();

		System.out.println("[in2] waiting for data...");
		/*
		 * if (socket.isClosed() || !socket.isConnected() ||
		 * socket.isInputShutdown() || socket.isOutputShutdown()) { this.running
		 * = false; return; }
		 */
		String packetRappresentation = in.readLine();
		if (packetRappresentation == null
				|| !community.existUserSocket(username)
				|| community.isUserSocketClosed(username)) {
			this.running = false;
			return;
		}
		System.out.println("[in2] " + packetRappresentation);

		Packet packet = this.createPacket(packetRappresentation);
		if (packet instanceof Logout) {
			running = false;
			if (community.existUserSocket(username))
				community.closeUserSocket(username);
		}

		packet.handlePacket(senderAddress);
		if (packet instanceof Login) {
			int result = packet.handlePacket(senderAddress);
			out.write(result);
			out.flush();
		}
	}

	@Override
	protected void finalize() throws Throwable {
		if (socket != null) {
			socket.getInputStream().close();
			socket.getOutputStream().close();
			socket.close();
		}
	}

	private Packet createPacket(String packetRappresentation) {
		Document doc = Packet
				.createXMLDocumentFromString(packetRappresentation);
		String rootTag = doc.getChildNodes().item(0).getNodeName();

		if (rootTag.equals(PacketType.REGISTRATION.toString())) {
			Registration r = Registration
					.createRegistration(packetRappresentation);
			// this.username = r.getUsername();
			return r;
		}
		if (rootTag.equals(PacketType.LOGIN.toString())) {
			Login l = Login.createLogin(packetRappresentation);
			this.username = l.getUsername();
			return l;
		}
		if (rootTag.equals(PacketType.LOGOUT.toString())) {
			Logout l = Logout.createLogout(packetRappresentation);
			this.username = l.getUsername();
			return l;
		}
		if (rootTag.equals(PacketType.MESSAGE.toString())) {
			Message m = Message.createMessage(packetRappresentation);
			this.username = m.getSender();
			return m;
		}
		if (rootTag.equals(PacketType.STATUS_NOTIFICATION.toString())) {
			StatusNotification s = StatusNotification
					.createStatusNotification(packetRappresentation);
			this.username = s.getOwner();
			return s;
		}

		this.username = null;
		return null;
	}
}